package com.github.xzb617.helper;

import com.github.xzb617.annotation.*;
import com.github.xzb617.conf.TreeConf;
import com.github.xzb617.exception.NoSuchTreeNodeException;
import com.github.xzb617.exception.TreeNodeCannotSetValueException;
import com.github.xzb617.func.FirstLevelNodeConsumer;
import com.github.xzb617.utils.ReflectUtil;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author xzb617
 * @date 2022/5/15 16:55
 * @description:
 */
public class TreeHelper {

    public static <T> List<T> createTree(List<T> nodes, boolean isSorted, boolean reverseOrder) {
        Stream<T> stream = nodes.stream()
                .filter(item -> {
                    // 寻找子节点
                    findChildrenNodes(nodes, item, isSorted, reverseOrder);
                    // 获取第一级节点
                    return !findParentNode(nodes, item);
                });


        if (isSorted) {
            Comparator<T> comparator = getSortedComparator(reverseOrder);
            stream = stream.sorted(comparator);
        }

        return stream.collect(Collectors.toList());
    }



    public static <T> List<T> createTree(List<T> nodes, boolean isSorted, boolean reverseOrder, FirstLevelNodeConsumer consumer) {
        Stream<T> stream = nodes.stream()
                .filter(item -> {
                    // 寻找子节点
                    findChildrenNodes(nodes, item, isSorted, reverseOrder);
                    // 获取第一级节点
                    return consumer.isFirstLevelNode(nodes, item);
                });


        if (isSorted) {
            Comparator<T> comparator = getSortedComparator(reverseOrder);
            stream = stream.sorted(comparator);
        }

        return stream.collect(Collectors.toList());
    }


    public static <T> List<T> createTree(List<T> nodes, boolean isSorted, boolean reverseOrder, FirstLevelNodeConsumer consumer, T mountIfNotFoundParent) {
        List<T> notFoundParentAndNotFirstLevelNodes = new ArrayList<>();
        Stream<T> stream = nodes.stream()
                .filter(item -> {
                    // 寻找子节点
                    findChildrenNodes(nodes, item, isSorted, reverseOrder);
                    // 获取第一级节点
                    boolean res = consumer.isFirstLevelNode(nodes, item);
                    if (!res && !findParentNode(nodes, item)) {
                        notFoundParentAndNotFirstLevelNodes.add(item);
                    }
                    return res;
                });

        // 一级节点、未找到父节点的节点 -> 排序
        if (isSorted) {
            Comparator<T> comparator = getSortedComparator(reverseOrder);
            stream = stream.sorted(comparator);
            notFoundParentAndNotFirstLevelNodes.sort(getSortedComparator(reverseOrder));
        }

        // 将未找到父节点切不是一级节点的节点挂载到指定节点下
        List<T> resultNodes = stream.collect(Collectors.toList());
        setChildrenNodes(mountIfNotFoundParent, notFoundParentAndNotFirstLevelNodes);
        resultNodes.add(mountIfNotFoundParent);
        return resultNodes;
    }


    public static <T> List<T> createTree(List<T> nodes, String idNode, String pidNode, String childrenNode, boolean isSorted, String orderNode, boolean reverseOrder) {
        Stream<T> stream = nodes.stream()
                .filter(item -> {
                    // 寻找子节点
                    findChildrenNodes(nodes, item, idNode, pidNode, childrenNode, orderNode, isSorted, reverseOrder);
                    // 获取第一级节点
                    return !findParentNode(nodes, item, idNode, pidNode);
                });

        if (isSorted) {
            Comparator<T> comparator = getSortedComparator(reverseOrder);
            stream = stream.sorted(comparator);
        }

        return stream.collect(Collectors.toList());
    }



    public static <T> List<T> createTree(List<T> nodes, String idNode, String pidNode, String childrenNode, boolean isSorted, String orderNode, boolean reverseOrder, FirstLevelNodeConsumer consumer) {
        Stream<T> stream = nodes.stream()
                .filter(item -> {
                    // 寻找子节点
                    findChildrenNodes(nodes, item, idNode, pidNode, childrenNode, orderNode, isSorted, reverseOrder);
                    // 获取第一级节点
                    return consumer.isFirstLevelNode(nodes, item);
                });

        if (isSorted) {
            Comparator<T> comparator = getSortedComparator(reverseOrder);
            stream = stream.sorted(comparator);
        }

        return stream.collect(Collectors.toList());
    }

    public static <T> List<T> createTree(List<T> nodes, String idNode, String pidNode, String childrenNode, boolean isSorted, String orderNode, boolean reverseOrder, FirstLevelNodeConsumer consumer, T mountIfNotFoundParent) {
        List<T> notFoundParentAndNotFirstLevelNodes = new ArrayList<>();
        Stream<T> stream = nodes.stream()
                .filter(item -> {
                    // 寻找子节点
                    findChildrenNodes(nodes, item, idNode, pidNode, childrenNode, orderNode, isSorted, reverseOrder);
                    // 获取第一级节点
                    boolean res = consumer.isFirstLevelNode(nodes, item);
                    if (!res && !findParentNode(nodes, item)) {
                        notFoundParentAndNotFirstLevelNodes.add(item);
                    }
                    return res;
                });

        // 一级节点、未找到父节点的节点 -> 排序
        if (isSorted) {
            Comparator<T> comparator = getSortedComparator(reverseOrder);
            stream = stream.sorted(comparator);
            notFoundParentAndNotFirstLevelNodes.sort(getSortedComparator(reverseOrder));
        }

        List<T> resultNodes = stream.collect(Collectors.toList());
        setChildrenNodes(mountIfNotFoundParent, notFoundParentAndNotFirstLevelNodes);
        resultNodes.add(mountIfNotFoundParent);
        return resultNodes;
    }

    public static <T> List<T> createTree(List<T> nodes, TreeConf treeConf) {
        return createTree(nodes, treeConf.getIdNode(), treeConf.getPidNode(), treeConf.getChildrenNode(), treeConf.getSorted(), treeConf.getOrderNode(), treeConf.getReverseOrder());
    }

    public static <T> List<T> createTree(List<T> nodes, TreeConf treeConf, FirstLevelNodeConsumer consumer) {
        return createTree(nodes, treeConf.getIdNode(), treeConf.getPidNode(), treeConf.getChildrenNode(), treeConf.getSorted(), treeConf.getOrderNode(), treeConf.getReverseOrder(), consumer);
    }

    public static <T> List<T> createTree(List<T> nodes, TreeConf treeConf, FirstLevelNodeConsumer consumer, T mountIfNotFoundParent) {
        return createTree(nodes, treeConf.getIdNode(), treeConf.getPidNode(), treeConf.getChildrenNode(), treeConf.getSorted(), treeConf.getOrderNode(), treeConf.getReverseOrder(), consumer, mountIfNotFoundParent);
    }


    public static <T> Set<T> calcLevelNodes(List<T> nodes, int level) {
        return nodes.stream()
                .filter(node -> {
                    Object nodeLevel = getNodeValue(node, TreeNodeLevel.class);
                    return nodeLevel!=null && (int)nodeLevel == level;
                })
                .collect(Collectors.toSet());
    }

    public static <T> Set<T> calcLevelNodes(List<T> nodes, long level) {
        return nodes.stream()
                .filter(node -> {
                    Object nodeLevel = getNodeValue(node, TreeNodeLevel.class);
                    return nodeLevel!=null && (long)nodeLevel == level;
                })
                .collect(Collectors.toSet());
    }

    public static <T, R> Set<R> calcLevelNodeIds(List<T> nodes, int level, boolean lessAndEqualThen, Function<T, R> mapFunc) {
        return nodes.stream()
                .filter(node -> {
                    Object nodeLevel = getNodeValue(node, TreeNodeLevel.class);
                    if (lessAndEqualThen) {
                        return nodeLevel!=null && (int)nodeLevel <= level;
                    }
                    return nodeLevel!=null && (int)nodeLevel == level;
                })
                .map(mapFunc)
                .collect(Collectors.toSet());
    }

    public static <T> Set<Integer> calcLevelNodeIdsToInteger(List<T> nodes, int level, boolean lessAndEqualThen) {
        return calcLevelNodeIds(nodes, level, lessAndEqualThen, node -> (int) getNodeValue(node, TreeNodeId.class));
    }

    public static <T> Set<Integer> calcLevelNodeIdsToInteger(List<T> nodes, int level, boolean lessAndEqualThen, String idNode) {
        return calcLevelNodeIds(nodes, level, lessAndEqualThen, node -> (int) getNodeValue(node, idNode));
    }

    public static <T> Set<Long> calcLevelNodeIdsToLong(List<T> nodes, int level, boolean lessAndEqualThen) {
        return calcLevelNodeIds(nodes, level, lessAndEqualThen, node -> (long) getNodeValue(node, TreeNodeId.class));
    }

    public static <T> Set<Long> calcLevelNodeIdsToLong(List<T> nodes, int level, boolean lessAndEqualThen, String idNode) {
        return calcLevelNodeIds(nodes, level, lessAndEqualThen, node -> (long) getNodeValue(node, idNode));
    }

    public static <T> Set<String> calcLevelNodeIdsToString(List<T> nodes, int level, boolean lessAndEqualThen) {
        return calcLevelNodeIds(nodes, level, lessAndEqualThen, node -> String.valueOf(getNodeValue(node, TreeNodeId.class)));
    }

    public static <T> Set<String> calcLevelNodeIdsToString(List<T> nodes, int level, boolean lessAndEqualThen, String idNode) {
        return calcLevelNodeIds(nodes, level, lessAndEqualThen, node -> String.valueOf(getNodeValue(node, idNode)));
    }


    public static <T> List<T> getChildrenNodes(List<T> nodes, T parentNode, boolean isSorted, boolean reverseOrder) {
        Stream<T> stream = nodes
                .stream()
                .filter(node -> getNodeValue(node, TreeNodeId.class) != null
                        && getNodeValue(node, TreeNodePid.class).equals(getNodeValue(parentNode, TreeNodeId.class)));
        if (isSorted) {
            Comparator<T> comparator = getSortedComparator(reverseOrder);
            stream = stream.sorted(comparator);
        }
        return stream.collect(Collectors.toList());
    }

    public static <T> List<T> getChildrenNodes(List<T> nodes, T parentNode, String idNode, String pidNode, String childrenNode, String orderNode, boolean isSorted, boolean reverseOrder) {
        Stream<T> stream = nodes
                .stream()
                .filter(node -> {
                    return getNodeValue(node, idNode)!=null
                            && getNodeValue(node, pidNode).equals(getNodeValue(parentNode, idNode));
                });
        if (isSorted) {
            Comparator<T> comparator = getSortedComparator(orderNode, reverseOrder);
            stream = stream.sorted(comparator);
        }
        return stream.collect(Collectors.toList());
    }


    private static <T> void findChildrenNodes(List<T> nodes, T parentNode, boolean isSorted, boolean reverseOrder) {
        setChildrenNodes(parentNode, getChildrenNodes(nodes, parentNode, isSorted, reverseOrder));
    }


    private static <T> void findChildrenNodes(List<T> nodes, T parentNode, String idNode, String pidNode, String childrenNode, String orderNode, boolean isSorted, boolean reverseOrder) {
        setChildrenNodes(parentNode, getChildrenNodes(nodes, parentNode, idNode, pidNode, childrenNode, orderNode, isSorted, reverseOrder), childrenNode);
    }


    private static <T> boolean findParentNode(List<T> nodes, T childNode) {
        return  nodes
                .stream()
                .anyMatch(node -> {
                    Object var1 = getNodeValue(node, TreeNodeId.class);
                    Object var2 = getNodeValue(childNode, TreeNodePid.class);
                    return var1!=null && var1.equals(var2);
                });
    }

    private static <T> boolean findParentNode(List<T> nodes, T childNode, String idNode, String pidNode) {
        return  nodes
                .stream()
                .anyMatch(node -> {
                    Object var1 = getNodeValue(node, idNode);
                    Object var2 = getNodeValue(childNode, pidNode);
                    return var1!=null && var1.equals(var2);
                });
    }

    private static<T> Comparator<T> getSortedComparator(boolean reverseOrder) {
        return Comparator.comparing((Function<T, Integer>) t -> {
                    Object var = getNodeValue(t, TreeNodeOrder.class);
                    if (var != null) {
                        return Integer.parseInt(String.valueOf(var));
                    }
                    return Integer.MAX_VALUE;
                },
                reverseOrder?Comparator.reverseOrder():Comparator.naturalOrder());
    }

    private static<T> Comparator<T> getSortedComparator(String orderNode, boolean reverseOrder) {
        return Comparator.comparing((Function<T, Integer>) t -> {
                    Object var = getNodeValue(t, orderNode);
                    if (var != null) {
                        return Integer.parseInt(String.valueOf(var));
                    }
                    return Integer.MAX_VALUE;
                },
                reverseOrder?Comparator.reverseOrder():Comparator.naturalOrder());
    }



    private static <T> void setChildrenNodes(T node, Object value) {
        try {
            ReflectUtil.setFieldValue(TreeNodeChildren.class, node, value);
        } catch (IllegalAccessException | IllegalArgumentException e) {
            throw new TreeNodeCannotSetValueException(e.getMessage(), e);
        } catch (NoSuchFieldException e) {
            throw new NoSuchTreeNodeException(e.getMessage(), e);
        }

    }

    private static <T> void setChildrenNodes(T node, Object value, String childrenNode) {
        try {
            ReflectUtil.setFieldValue(childrenNode, node, value);
        } catch (IllegalAccessException | IllegalArgumentException e) {
            throw new TreeNodeCannotSetValueException(e.getMessage(), e);
        } catch (NoSuchFieldException e) {
            throw new NoSuchTreeNodeException(e.getMessage(), e);
        }
    }


    private static <T> Object getNodeValue(T node, Class<? extends Annotation> ann) {
        try {
            return ReflectUtil.getFieldValue(ann, node);
        } catch (IllegalAccessException | IllegalArgumentException e) {
            throw new TreeNodeCannotSetValueException(e.getMessage(), e);
        } catch (NoSuchFieldException e) {
            throw new NoSuchTreeNodeException(e.getMessage(), e);
        }
    }

    private static <T> Object getNodeValue(T node, String nodeName) {
        try {
            return ReflectUtil.getFieldValue(nodeName, node);
        } catch (IllegalAccessException | IllegalArgumentException e) {
            throw new TreeNodeCannotSetValueException(e.getMessage(), e);
        } catch (NoSuchFieldException e) {
            throw new NoSuchTreeNodeException(e.getMessage(), e);
        }
    }
}
